package com.fubowen.reader.adapter

import android.graphics.Rect
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.OrientationHelper
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.max

class AutoWrapLayoutManager(val horizontalGap: Int = 0, val verticalGap: Int = 0) : RecyclerView.LayoutManager() {

    private val maxWidthPercent = 0.5
    private val currentLine = mutableListOf<Pair<View, Rect>>()
    private val orientationHelper: OrientationHelper = OrientationHelper.createVerticalHelper(this)
    private val positionCache = mutableMapOf<Int, Pair<Int, IntRange>>()
    private val maxW: Int get() = (width - paddingLeft - paddingRight)
    private val maxH: Int get() = (height - paddingTop - paddingBottom)

    //region 生命周期方法
    override fun generateDefaultLayoutParams() = RecyclerView.LayoutParams(
        RecyclerView.LayoutParams.WRAP_CONTENT,
        RecyclerView.LayoutParams.WRAP_CONTENT
    )

    override fun isAutoMeasureEnabled() = true
    override fun canScrollVertically() = false
    override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
        if (childCount == 0 && state.isPreLayout) {
            return
        }
        detachAndScrapAttachedViews(recycler)
        positionCache.clear()

        val dd = split(maxW, 24 * 3, 16 * 3, paddingLeft)

        extracted(recycler, dd, paddingTop, 0)

//        var unusedHeight = maxH
//        var nextTop = paddingTop
//        var index = 0
//        var position = 0
//        do {
//            val options = appendLineToBottom(position, recycler, nextTop, index)
//            val range = position until options.position
//            for (i in range) {
//                positionCache[i] = Pair(index, range)
//            }
//            position = options.position
//            unusedHeight -= options.lineHeight
//            nextTop += options.lineHeight
//            index++
//        } while (unusedHeight > 0 && position < itemCount)
    }

    private fun extracted(recycler: RecyclerView.Recycler, dd: List<Grid>, top: Int, position: Int) {
        var usedWidth = 0
        var maxLineHeight = 0
        for (i in position until itemCount) {
            val itemView = getItemView(recycler, i)

            val left = dd.find { it.start >= usedWidth }
            if (left == null) {
                extracted(recycler, dd, top + verticalGap + maxLineHeight, i)
                break
            }
            val rig = dd.find { it.end >= (left.start + itemView.width) }
            if (rig == null) {
                extracted(recycler, dd, top + verticalGap + maxLineHeight, i)
                break
            }
            usedWidth = rig.end
            maxLineHeight = max(maxLineHeight, itemView.height)
            val rect = Rect(left.start, top, rig.end, top + itemView.height)
           
            layoutDecorated(itemView.view, left.start, top, rig.end, top + itemView.height)
            itemView.view.updateLayoutParams<MarginLayoutParams> {
                width = rect.width()
                itemView.view.requestLayout()
            }
        }
    }

    override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler, state: RecyclerView.State): Int {
        if (childCount == 0 || dy == 0) {
            return 0
        }
        removeInvisibleView(dy, recycler)
        val offset = insertShouldVisibleView(dy, recycler)
        offsetChildrenVertical(-offset)
        return offset
    }

    //endregion

    //region 获取指定位置的子视图
    fun getItemView(recycler: RecyclerView.Recycler, position: Int, isBottom: Boolean = true): ItemView {
        if (isBottom) {
            return getItemView(recycler, position, -1)
        }
        return getItemView(recycler, position, 0)
    }

    fun getItemView(recycler: RecyclerView.Recycler, position: Int, index: Int): ItemView {
        val view = recycler.getViewForPosition(position)
        addView(view, index)
        measureChildWithMargins(view, 0, 0)
        val w = getDecoratedMeasuredWidth(view)
        val h = getDecoratedMeasuredHeight(view)
        return ItemView(view, h, w)
    }

    //endregion

    //region 向列表追加一行子视图

    private fun appendLineToBottom(position: Int, recycler: RecyclerView.Recycler, top: Int, lineNumber: Int): AppendResult {
        val hGap = if (lineNumber == 0) 0 else horizontalGap
        var itemView = getItemView(recycler, position)
        val isSingleLine = itemView.width >= maxW * maxWidthPercent
        if (isSingleLine) {
            val l = paddingLeft
            val r = paddingLeft + maxW
            val t = top + hGap
            val b = t + itemView.height
            val rect = Rect(l, t, r, b)
            super.layoutDecorated(itemView.view, rect.left, rect.top, rect.right, rect.bottom)
            return AppendResult(position + 1, itemView.height + hGap)
        }
        val l = paddingLeft
        val r = l + itemView.width
        val t = top + hGap
        val b = t + itemView.height
        val rect = Rect(l, t, r, b)
        super.layoutDecorated(itemView.view, rect.left, rect.top, rect.right, rect.bottom)
        currentLine.add(Pair(itemView.view, rect))
        var usedWidth = itemView.width + paddingLeft
        var maxLineHeight = itemView.height
        val wGap = verticalGap
        var lastPosition = position

        for (i in position + 1 until itemCount) {
            itemView = getItemView(recycler, i)
            val needWidth = usedWidth + wGap + itemView.width
            if (needWidth <= maxW) {
                val l = usedWidth + wGap
                val r = l + itemView.width
                val t = top + hGap
                val b = t + itemView.height
                val rect = Rect(l, t, r, b)
                super.layoutDecorated(itemView.view, rect.left, rect.top, rect.right, rect.bottom)
                usedWidth += wGap + itemView.width
                maxLineHeight = max(maxLineHeight, itemView.height)
                lastPosition = i
                currentLine.add(Pair(itemView.view, rect))
            } else {
                removeAndRecycleView(itemView.view, recycler)
                break
            }
        }
        spaceBetween(usedWidth)
        return AppendResult(lastPosition + 1, maxLineHeight + hGap)
    }

    private fun appendLineToTop(position: Int, recycler: RecyclerView.Recycler, top: Int, lineNumber: Int): AppendResult {
        val hGap = if (lineNumber == 0) 0 else horizontalGap
        var itemView = getItemView(recycler, position, false)
        val isSingleLine = itemView.width >= maxW * maxWidthPercent
        if (isSingleLine) {
            val l = paddingLeft
            val r = l + maxW
            val t = top - hGap - itemView.height
            val b = top - hGap
            val rect = Rect(l, t, r, b)
            super.layoutDecorated(itemView.view, rect.left, rect.top, rect.right, rect.bottom)
            return AppendResult(position - 1, itemView.height + hGap)
        }
        val l = width - paddingRight - itemView.width
        val r = width - paddingRight
        val t = top - hGap - itemView.height
        val b = top - hGap
        val rect = Rect(l, t, r, b)
        super.layoutDecorated(itemView.view, rect.left, rect.top, rect.right, rect.bottom)

        var usedWidth = itemView.width + paddingRight
        var maxLineHeight = itemView.height
        val wGap = verticalGap
        var lastPosition = position
        for (i in position - 1 downTo 0) {
            itemView = getItemView(recycler, i, false)
            val needWidth = usedWidth + wGap + itemView.width
            if (needWidth <= maxW) {
                val l = width - usedWidth - wGap - itemView.width
                val r = width - usedWidth - wGap
                val t = top - hGap - itemView.height
                val b = top - hGap
                val rect = Rect(l, t, r, b)
                super.layoutDecorated(itemView.view, rect.left, rect.top, rect.right, rect.bottom)
                usedWidth += wGap + itemView.width
                maxLineHeight = max(maxLineHeight, itemView.height)
                lastPosition = i
            } else {
                removeAndRecycleView(itemView.view, recycler)
                break
            }
        }
        return AppendResult(lastPosition - 1, maxLineHeight + hGap)
    }

    private fun recoverLine(position: Int, recycler: RecyclerView.Recycler, topEdge: Int) {
        val lineInfo = positionCache[position] ?: return
        val (_, range) = lineInfo

        val hGap = horizontalGap
        var itemView = getItemView(recycler, range.first, 0)
        val isSingleLine = itemView.width >= maxW * maxWidthPercent
        if (isSingleLine) {
            val l = paddingLeft
            val r = l + maxW
            val t = topEdge - hGap - itemView.height
            val b = topEdge - hGap
            val rect = Rect(l, t, r, b)
            super.layoutDecorated(itemView.view, rect.left, rect.top, rect.right, rect.bottom)
            return
        }

        val l = paddingLeft
        val r = l + itemView.width
        val t = topEdge - hGap - itemView.height
        val b = topEdge - hGap
        val rect = Rect(l, t, r, b)
        super.layoutDecorated(itemView.view, rect.left, rect.top, rect.right, rect.bottom)
        currentLine.add(Pair(itemView.view, rect))

        var usedWidth = paddingLeft + itemView.width
        var maxLineHeight = itemView.height
        val wGap = verticalGap

        var idx = 1
        for (i in range.first + 1..range.last) {
            itemView = getItemView(recycler, i, idx)
            val needWidth = usedWidth + wGap + itemView.width
            if (needWidth <= maxW) {
                val l = usedWidth + wGap
                val r = l + itemView.width
                val t = topEdge - hGap - itemView.height
                val b = topEdge - hGap
                val rect = Rect(l, t, r, b)
                super.layoutDecorated(itemView.view, rect.left, rect.top, rect.right, rect.bottom)
                usedWidth += wGap + itemView.width
                maxLineHeight = max(maxLineHeight, itemView.height)
                idx++
                currentLine.add(Pair(itemView.view, rect))
            } else {
                removeAndRecycleView(itemView.view, recycler)
                break
            }
        }
        spaceBetween(usedWidth)
    }

    //endregion

    //region 回收视图，插入视图
    private fun insertShouldVisibleView(dy: Int, recycler: RecyclerView.Recycler): Int {
        if (dy > 0) {
            val end = getChildAt(childCount - 1)
            val endEdge = orientationHelper.getDecoratedEnd(end)
            if (end != null) {
                val endPosition = getPosition(end)
                if (endPosition < itemCount - 1) {
                    if (endEdge - dy < orientationHelper.endAfterPadding) {
                        val result = appendLineToBottom(endPosition + 1, recycler, endEdge, 1)
                        val lineInfo = positionCache[endPosition]
                        if (lineInfo != null) {
                            val range = endPosition + 1 until result.position
                            for (i in range) {
                                positionCache[i] = Pair(lineInfo.first + 1, range)
                            }
                        }
                    }
                } else {
                    if (endEdge <= orientationHelper.endAfterPadding) {
                        return 0
                    }
                    val offset = endEdge - orientationHelper.endAfterPadding
                    if (offset >= dy) {
                        return dy
                    }
                    return offset
                }
            }
        } else {
            val start = getChildAt(0)
            val startEdge = orientationHelper.getDecoratedStart(start)
            if (start != null) {
                val startPosition = getPosition(start)
                if (startPosition > 0) {
                    if (startEdge - dy > orientationHelper.startAfterPadding) {
                        recoverLine(startPosition - 1, recycler, startEdge)
                    }
                } else {
                    if (startEdge >= orientationHelper.startAfterPadding) {
                        return 0
                    }
                    val offset = startEdge - orientationHelper.startAfterPadding
                    if (offset <= dy) {
                        return dy
                    }
                    return offset
                }
            }
        }
        return dy
    }

    private fun removeInvisibleView(dy: Int, recycler: RecyclerView.Recycler) {
        val startEdgeLimit = orientationHelper.startAfterPadding
        val endEdgeLimit = orientationHelper.endAfterPadding
        if (dy > 0) {
            for (i in 0 until childCount) {
                val view = getChildAt(i)
                if (view != null) {
                    val endEdge = orientationHelper.getDecoratedEnd(view)
                    if (endEdge - dy < startEdgeLimit) {
                        removeAndRecycleView(view, recycler)
                    } else {
                        break
                    }
                }
            }
        } else {
            for (i in childCount - 1 downTo 0) {
                val view = getChildAt(i)
                if (view != null) {
                    val startEdge = orientationHelper.getDecoratedStart(view)
                    if (startEdge - dy > endEdgeLimit) {
                        removeAndRecycleView(view, recycler)
                    } else {
                        break
                    }
                }
            }
        }

    }

    private fun spaceBetween(usedWidth: Int) {
//        val freeSpace = width - paddingRight - usedWidth
//        if (freeSpace > 0) {
//            val gapCount = currentLine.size - 1
//            val extra = freeSpace / gapCount
//            for (i in 1..gapCount) {
//                val (view, rect) = currentLine[i]
//                rect.left += extra * i
//                rect.right += extra * i
//                layoutDecorated(view, rect.left, rect.top, rect.right, rect.bottom)
//            }
//        }
//        currentLine.clear()

        val freeSpace = width - paddingRight - usedWidth
        if (freeSpace > 0) {
            val gapCount = currentLine.size
            val extra = freeSpace / gapCount
            for (i in 0..<gapCount) {
                val (view, rect) = currentLine[i]
                rect.left += extra * i
                rect.right += extra * (i + 1)
                layoutDecorated(view, rect.left, rect.top, rect.right, rect.bottom)
            }
        }
        currentLine.clear()
    }
    //endregion

    //region 方法返回数据类
    data class ItemView(
        var view: View,
        var height: Int,
        var width: Int
    )

    data class AppendResult(
        var position: Int,
        var lineHeight: Int
    )
    //endregion

    fun split(width: Int, grid: Int, gap: Int, offset: Int): List<Grid> {
        val num = (width + gap) / (grid + gap)
        val freeSpace = width - (num * grid + num * gap - gap)
        val extraSpace = freeSpace / num
        val newGrid = grid + extraSpace
        return (0..width).chunked(newGrid + gap).map { Grid(it.first() + offset, it.last() + offset - gap) }.apply {
            last().end += gap
        }
    }

    data class Grid(
        var start: Int,
        var end: Int,
    )
}